/*
	update.c	
	
	Handles conversion of data into network byte order and vice versa.
*/

#include 	<stdio.h>
#include 	<stdlib.h>
#include 	<string.h>
#include 	<stdint.h>
#include 	<sys/types.h>
#include 	<sys/uio.h>
#include 	<unistd.h>
#include 	<arpa/inet.h>
#include 	<errno.h>
#include 	"types.h"
#include 	"protocol.h"
#include 	"player.h"
#include 	"net.h"
#include	"state.h"
#include	"object.h"

/* declare global variables */
int updateState;
Team Teams[NUMTEAMS];
Object Objects[NUMOBJS];

/* Methods to marshall updates and send them to the client */

/* Send the specified CellUpdateBody to all clients. */
void sendCellUpdateToAll( CellUpdateBody *cub ) 
{
	// set cell update packet fields
	int numCellUpdates = 1;
	int bodyLength = CELL_UPDATE_HEADER_LENGTH + 
					( numCellUpdates * CELL_UPDATE_BODY_LENGTH );
	
	// send buffer
	char data[CTF_HEADER_LENGTH + bodyLength +1];
	
	printf("cell update\n");
	
	// build ctf packet header
	*( (short *) &data[0] ) = htons( 0 			 );
	*( (short *) &data[2] ) = htons( CELL_UPDATE );
	*(  (long *) &data[4] ) = htonl( updateState );
	*(  (long *) &data[8] ) = htonl( bodyLength  );
	
	// append cell update header
	*(  (long *) &data[12] ) = htonl(numCellUpdates);
	
	// append 1 cell update
	*( (short *) &data[16] ) = htons( cub->pos.row 			);
	*( (short *) &data[18] ) = htons( cub->pos.col 			);
	*( (short *) &data[20] ) = htons( cub->cellType 		);
	*( (short *) &data[22] ) = htons( cub->occupiedByPlayer );
	*( (short *) &data[24] ) = htons( cub->occupiedByObject );
	
	// for each client, send buffer to its socket
	int i;
	for ( i = 0; i < NUMTEAMS; i++ ) 
	{
		Player *playerPtr = Teams[i].playerListPtr;
		if ( playerPtr == NULL )	// end of list
			continue;	
		do 
		{
			// socket desciptor of Player i
			int sock = playerPtr->socketId;
			// write cell update packet to this socket descriptor
			net_writen( sock, data, CTF_HEADER_LENGTH + bodyLength );
			// move to the next player in the list
			playerPtr = playerPtr->next;
		} while ( playerPtr != NULL);
	}
}

/* Broadcasts a message of length fromm senderId to all clients. */
void broadcastChatMsg( char *msg, int length, int senderId ) 
{	
	// leave room for message length field
	int bodyLength = length + 2;
	char data[CTF_HEADER_LENGTH + bodyLength +1];
	
	printf("broadcast chat\n");
	
	// ctf packet header
	*( (short *) &data[0]  ) = htons( 0 				 );
	*( (short *) &data[2]  ) = htons( CHAT_MSG_BROADCAST );
	*(  (long *) &data[4]  ) = htonl( updateState 	     );	
	*(  (long *) &data[8]  ) = htonl( bodyLength 		 );
	*( (short *) &data[12] ) = htons( senderId 			 );
	
	// copy message chars to send buffer
	memcpy( &data[14], msg, bodyLength );
	
	// for each client, send buffer to its socket
	int i;
	for ( i = 0; i < NUMTEAMS; i++ ) 
	{
		Player *playerPtr = Teams[i].playerListPtr;
		if ( playerPtr == NULL )	// end of list
			continue;	
		do 
		{
			// socket desciptor of Player i
			int sock = playerPtr->socketId;
			// write cell update packet to this socket descriptor
			net_writen( sock, data, CTF_HEADER_LENGTH + bodyLength );
			// move to the next player in the list
			playerPtr = playerPtr->next;
		} while ( playerPtr != NULL);
	}
}

/* Send packets containing all player and object positions. */
void sendInitialCellUpdates() {
	// first update
	updateState++;
	
	// Loop to send updates of all players
	int i;
	for ( i = 0; i < NUMTEAMS; i++ ) 
	{
		Player *playerPtr = Teams[i].playerListPtr;
		if ( playerPtr == NULL )
			continue;

		do
		{
			int cType;
			if ( 		CELL_IS_FREE(playerPtr->cellPtr) )
				cType = 0;
			else if (   CELL_IS_WALL(playerPtr->cellPtr) )
				cType = 1;
			else if (   CELL_IS_A_HOME(playerPtr->cellPtr) )
				cType = 2;
			else if (   CELL_IS_A_JAIL(playerPtr->cellPtr) )
				cType = 3;
			
			// create a cell update body for this information
			CellUpdateBody cub;
			cub.pos.row 		 = playerPtr->cellPtr->pos.row ;
			cub.pos.col 		 = playerPtr->cellPtr->pos.col ;
			cub.cellType 		 = cType;
			cub.occupiedByPlayer = playerPtr->id ;
			cub.occupiedByObject = 0;
			
			// send this cell update to all players
			sendCellUpdateToAll( &cub );
			
			playerPtr = playerPtr->next;
		} while ( playerPtr != NULL);
	}
	
	
	// Loop to send updates of all objects
	for( i = 0; i < NUMOBJS; i++ ) {
		// get address of object
		Object *objPtr = &Objects[i];
		
		// make cell update body
		CellUpdateBody cub2;
		cub2.pos.row = objPtr->cellPtr->pos.row ;
		cub2.pos.col = objPtr->cellPtr->pos.col ;
		cub2.cellType =0;
		cub2.occupiedByPlayer = 0;
		cub2.occupiedByObject = (i+1) ;	// type of the object
		
		// send this cell update to all players
		sendCellUpdateToAll( &cub2 );			
	}
	
}

/*   */
void sendCellUpdateMove( int sockfd, Position p, int playerId ) {
	// number of updates
	int numCellUpdates = 1;
	int bodyLength = CELL_UPDATE_HEADER_LENGTH + (numCellUpdates*CELL_UPDATE_BODY_LENGTH);
	char data[CTF_HEADER_LENGTH + bodyLength +1];
	
	// ctf packet header
	*( (short *) &data[0] ) = htons( 0 			 );
	*( (short *) &data[2] ) = htons( CELL_UPDATE );
	*(  (long *) &data[4] ) = htonl( updateState );	
	*(  (long *) &data[8] ) = htonl( bodyLength  );
	
	// appends body length field
	*( (long *) &data[12] ) = htonl(numCellUpdates);
	
	// appends 1 update
	*( (short *) &data[16] ) = htons( p.row    );	// row
	*( (short *) &data[18] ) = htons( p.col    );	// column
	*( (short *) &data[20] ) = htons( 0 	   );	// game state
	*( (short *) &data[22] ) = htons( playerId );	// occupiedByPlayer
	*( (short *) &data[24] ) = htons( 0 	   );	// occupiedByItem
	
	// send cell update packet to the requestor socket
	net_writen( sockfd, data, CTF_HEADER_LENGTH + bodyLength );
}

/* Sends a player update packet to all clients. */
void sendPlayerUpdateToAll( PlayerUpdateBody *pub ) {
	// this is the first major change.
	updateState++;
	// initialize number of updates
	int numPlayerUpdates = 1;
	int bodyLength =  PLAYER_UPDATE_HEADER_LENGTH + (numPlayerUpdates*PLAYER_UPDATE_BODY_LENGTH);
	
	char data[CTF_HEADER_LENGTH + bodyLength +1];
	
	// ctf packet header
	*( (short *) &data[0] ) = htons( 0			   );
	*( (short *) &data[2] ) = htons( PLAYER_UPDATE );
	*(  (long *) &data[4] ) = htonl( updateState   );	
	*(  (long *) &data[8] ) = htonl( bodyLength    );
	
	// leave room for player update header
	*( (short *) &data[12] ) = htons(numPlayerUpdates);
	// player update body
	*( (short *) &data[14] ) = htons( pub->playerId );
	*( (short *) &data[16] ) = htons( pub->field 	);
	*( (short *) &data[18] ) = htons( pub->value 	);
	
	// for each client, send buffer to its socket
	int i;
	for ( i = 0; i < NUMTEAMS; i++ ) 
	{
		Player *playerPtr = Teams[i].playerListPtr;
		if ( playerPtr == NULL )	// end of list
			continue;	
		do 
		{
			// socket desciptor of Player i
			int sock = playerPtr->socketId;
			// send player update packet to the requestor socket
			net_writen( sock, data, CTF_HEADER_LENGTH + bodyLength );
			// move to the next player in the list
			playerPtr = playerPtr->next;
		} while ( playerPtr != NULL);
	}

}


/* Sends a game update body. */
void sendGameUpdateToAll( int sockfd, int gameState ) {
	updateState++;
	char data[CTF_HEADER_LENGTH + GAME_UPDATE_BODY_LENGTH +1];
	printf("game update\n");
	// ctf packet header
	*( (short *) &data[0] ) = htons( 0 			 );
	*( (short *) &data[2] ) = htons( GAME_UPDATE );
	*(  (long *) &data[4] ) = htonl( updateState );	
	*(  (long *) &data[8] ) = htonl( GAME_UPDATE_BODY_LENGTH );
	
	// game state
	*( (short *) &data[12] ) = htons( gameState );
	
	// send out to all clients
	int i;
	for (i = 0; i < NUMTEAMS; i++){
		Player *playerPtr = Teams[i].playerListPtr;
		if ( playerPtr == NULL ) // end of list
			continue;	
		do 
		{
			// socket my client is connecting on
			int sock = playerPtr->socketId;
			
			// send player update packet when the game begins.
			net_writen( sock, data, CTF_HEADER_LENGTH + GAME_UPDATE_BODY_LENGTH );

			playerPtr = playerPtr->next;
		} while ( playerPtr != NULL);
	}
}
